package com.example.mq.mqserver.core;

import com.example.mq.common.MqException;

/**
 * @Description:实现交换机的转发规则
 * 验证bindingKey是否合法
 * @Author: liudong
 * @Time: 2024/5/16
 */

public class Router {
    //bindingKey的构造规则：
    //数字，字母，下划线
    //点 . 分割成若干部分
    //允许存在 * 和 # 作为通配符
    public boolean checkBindingKey(String bindingKey){
        if (bindingKey.length() == 0){
            //空字符串，合法，如在使用direct交换机时，bindingKey用不上
            return true;
        }
        for (int i = 0;i<bindingKey.length();i++){
            char ch =bindingKey.charAt(i);
            //判断该字符是否是大写字母
            if (ch>='A' && ch<= 'Z'){
                continue;
            }
            if (ch>='a' && ch <='z'){
                continue;
            }
            if (ch>='0' && ch <='9'){
                continue;
            }
            if (ch =='.'||ch=='_'||ch =='*'||ch=='#'){
                continue;
            }
            //其他字符都不合法
            return false;
        }
        //检查*或者#是否是独立的部分

        String[] words = bindingKey.split("\\.");
        for (String word : words){
            //检查 word长度>1 并且包含了*或者#，就是非法的格式了
            if (word.length()>1 && (word.contains("*")||word.contains("#"))){
                return false;
            }
        }
        //约定一下，通配符之间的相邻关系
        //*和*之间，#和#之间，*和#之间，不允许相邻，均为非法
        for (int i = 0; i < words.length - 1; i++) {
            // 连续两个 ##
            if (words[i].equals("#") && words[i + 1].equals("#")) {
                return false;
            }
            // # 连着 *
            if (words[i].equals("#") && words[i + 1].equals("*")) {
                return false;
            }
            // * 连着 #
            if (words[i].equals("*") && words[i + 1].equals("#")) {
                return false;
            }
        }
        return true;
    }

    //检查routingKey是否合法
    //routingKey的构造规则：
    //数字，字母，下划线
    //点 . 分割成若干部分
    public boolean checkRoutingKey(String routingKey){
        if (routingKey.length() == 0){
            //空字符串，合法，如在使用fanout交换机时，routingKey用不上
            return true;
        }
        for (int i = 0;i<routingKey.length();i++){
            char ch =routingKey.charAt(i);
            //判断该字符是否是大写字母
            if (ch>='A' && ch<= 'Z'){
                continue;
            }
            if (ch>='a' && ch <='z'){
                continue;
            }
            if (ch>='0' && ch <='9'){
                continue;
            }
            if (ch =='.'||ch=='_'){
                continue;
            }
           //其他字符都不合法
            return false;
        }
        return true;
    }

    //判断bindingKey是否合法，并且是否和routingKey匹配
    //判断当前消息能否转发给绑定对应的队列
    public boolean route(ExchangeType exchangeType,Binding binding,Message message) throws MqException {
        //根据不同的exchangeType使用不同的转发规则
        if (exchangeType == ExchangeType.FANOUT) {
            return true;
        }else if (exchangeType == ExchangeType.TOPIC){
            return routeTopic(binding,message);
        }else {
            throw  new MqException("[Router] 交换机类型非法！exchangeType= "+exchangeType);
        }
    }

    //topic交换机转发规则
    //核心思路：类似于双指针
    private boolean routeTopic(Binding binding, Message message) {
        //先把这两个Key进行切分
        String[] bindingTokens = binding.getBindingKey().split("\\.");
        String[] routingTokens = message.getRoutingKey().split("\\.");
        //引入两个下标，指向上述数组，初始情况下都为0
        int bindingIndex = 0;
        int routingIndex = 0;
        //每次循环下标不一定加一，用while更好
        while (bindingIndex < bindingTokens.length && routingIndex <routingTokens.length){
            if (bindingTokens[bindingIndex].equals("*")){
                //1.如果遇到*，直接进入下一轮，*可匹配到任意一个部分
                bindingIndex++;
                routingIndex++;
            }else if (bindingTokens[bindingIndex].equals("#")){
                //如果遇到# ，先看有没有下一个位置
                bindingIndex++;
                if (bindingIndex == bindingTokens.length){
                    //2.如果#后面没东西了，说明此时一定能匹配成功！
                    return true;
                }
                //3.如果#后面还有东西，拿着这个内容，去routingKey中往后找，找到对应的位置。
                //findNextMatch 这个方法用来查找该部分在routingKey上的位置，返回该下标，没找到，返回-1
               routingIndex = findNextMatch(routingTokens,routingIndex,bindingTokens[bindingIndex]);
                if (routingIndex ==-1){
                    //没找到匹配的结果，匹配失败
                    return false;
                }
                //找到匹配的情况，继续往后匹配
                bindingIndex++;
                routingIndex++;
            }else {
                //4.如果遇到普通字符串，要求两边的内容是一样的
                if (!bindingTokens[bindingIndex].equals(routingTokens[routingIndex])){
                    return false;
                }
                bindingIndex++;
                routingIndex++;
            }
        }
        //5.判断双方是否同时到达末尾
        if (bindingIndex == bindingTokens.length && routingIndex == routingTokens.length){

            return true;
        }
        return false;
    }

    private int findNextMatch(String[] routingTokens, int routingIndex, String bindingToken) {
        for (int i = routingIndex; i <routingTokens.length ; i++) {
            if (routingTokens[i].equals(bindingToken)) {
                return i;
            }
        }
        return -1;
    }
}
